/* Esptouch example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_wpa2.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_pm.h"
#include "esp_smartconfig.h"
#include "driver/ledc.h"
#include "esp_err.h"

#define DEFAULT_LISTEN_INTERVAL CONFIG_EXAMPLE_WIFI_LISTEN_INTERVAL

#if CONFIG_EXAMPLE_POWER_SAVE_MIN_MODEM
#define DEFAULT_PS_MODE WIFI_PS_MIN_MODEM
#elif CONFIG_EXAMPLE_POWER_SAVE_MAX_MODEM
#define DEFAULT_PS_MODE WIFI_PS_MAX_MODEM
#elif CONFIG_EXAMPLE_POWER_SAVE_NONE
#define DEFAULT_PS_MODE WIFI_PS_NONE
#else
#define DEFAULT_PS_MODE WIFI_PS_NONE
#endif /*CONFIG_POWER_SAVE_MODEM*/

/*
    LEDC Config
*/
#if CONFIG_IDF_TARGET_ESP32
#define LEDC_HS_TIMER LEDC_TIMER_0
#define LEDC_HS_MODE LEDC_HIGH_SPEED_MODE
#define LEDC_HS_CH0_GPIO (2)
#define LEDC_HS_CH0_CHANNEL LEDC_CHANNEL_0
// #define LEDC_HS_CH1_GPIO       (19)
// #define LEDC_HS_CH1_CHANNEL    LEDC_CHANNEL_1
#endif
#define LEDC_TEST_DUTY (4000)
#define LEDC_TEST_FADE_TIME (3000)

/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t s_wifi_event_group;

//
static TaskHandle_t ledc_task_1;

/* The event group allows multiple bits for each event,
   but we only care about one event - are we connected
   to the AP with an IP? */
static const int CONNECTED_BIT = BIT0;
static const int ESPTOUCH_DONE_BIT = BIT1;
static esp_err_t nvs_err;
static const char *TAG = "smartconfig_example";

static void smartconfig_example_task(void *parm);
// static void ledc_task(ledc_channel_config_t ledc_channel, void *parm);

static void ledc_task();

/* 写入NVS存储方法
 */
static void nvs_write(esp_err_t err, const char *nvs_write_key, uint8_t *write_value)
{
    // Open
    printf("\n");
    ESP_LOGI("NVS", "Opening Non-Volatile Storage (NVS) handle... ");
    printf("\n");
    // printf("Opening Non-Volatile Storage (NVS) handle... ");
    nvs_handle_t my_handle;
    err = nvs_open("storage", NVS_READWRITE, &my_handle);
    if (err != ESP_OK)
    {
        printf("Error (%s) opening NVS handle!\n", esp_err_to_name(err));
    }
    else
    {
        // int32_t restart_counter = 0; // value will default to 0, if not set yet in NVS
        // Write
        // printf("Updating %s : %s in NVS ... ", nvs_write_key , write_value);

        // ESP_LOGI("nvs::nvs_write_key", "%s", nvs_write_key);
        ESP_LOGI(TAG, "SSID:%s", write_value);
        // ESP_LOGI("nvs::write_value", "%s", write_value);

        // restart_counter++;
        // err = nvs_set_i32(my_handle, nvs_write_key, write_value);

        // err = nvs_set_i8(my_handle, nvs_write_key, write_value);
        char write_value_s[] = "";
        sprintf(write_value_s, "%s", write_value);
        ESP_LOGI(TAG, "SSID(write_value_s):%s", write_value_s);
        err = nvs_set_str(my_handle, nvs_write_key, write_value_s);

        printf((err != ESP_OK) ? "Failed!\n" : "Done\n");
        // printf("%x", write_value);
        printf("\n");
        // Commit written value.
        // After setting any values, nvs_commit() must be called to ensure changes are written
        // to flash storage. Implementations may write to storage at other times,
        // but this is not guaranteed.
        printf("Committing updates in NVS ... ");
        err = nvs_commit(my_handle);
        printf((err != ESP_OK) ? "Failed!\n" : "Done\n");
    }

    // Close
    nvs_close(my_handle);
}

static void event_handler(void *arg, esp_event_base_t event_base,
                          int32_t event_id, void *event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
    {
        xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);
        
    }
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        esp_wifi_connect();
        xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
    }
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {
        xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
    }
    else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE)
    {
        ESP_LOGI(TAG, "Scan done");
        // int LEDC_T_F_T = 500;
        xTaskCreate(ledc_task, "ledc_task", 1024 * 2, (void*)3000, 3, &ledc_task_1); // 开始闪烁呼吸灯
    }
    else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL)
    {
        ESP_LOGI(TAG, "Found channel");
        vTaskDelete(ledc_task_1);
        xTaskCreate(ledc_task, "ledc_task", 1024 * 2, (void*)1000, 3, &ledc_task_1); // 开始闪烁呼吸灯

    }
    else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD)
    {
        ESP_LOGI(TAG, "Got SSID and password");
        vTaskDelete(ledc_task_1);
        xTaskCreate(ledc_task, "ledc_task", 1024 * 2, (void*)100, 3, &ledc_task_1); // 开始闪烁呼吸灯

        smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
        wifi_config_t wifi_config;
        uint8_t ssid[33] = {0};
        uint8_t password[65] = {0};
        uint8_t rvd_data[33] = {0};

        bzero(&wifi_config, sizeof(wifi_config_t));
        memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
        memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));
        wifi_config.sta.bssid_set = evt->bssid_set;
        if (wifi_config.sta.bssid_set == true)
        {
            memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
        }

        memcpy(ssid, evt->ssid, sizeof(evt->ssid));
        memcpy(password, evt->password, sizeof(evt->password));
        // ESP_LOGI(TAG, "原功能完成。");
        // memcpy(wifi_config.sta.listen_interval, DEFAULT_LISTEN_INTERVAL, sizeof(DEFAULT_LISTEN_INTERVAL));
        ESP_LOGI(TAG, "SSID:%s", ssid);
        //写入NVS存储
        nvs_write(nvs_err, "SSID", ssid);
        ESP_LOGI(TAG, "PASSWORD:%s", password);
        nvs_write(nvs_err, "PASSWORD", password);
        if (evt->type == SC_TYPE_ESPTOUCH_V2)
        {
            ESP_ERROR_CHECK(esp_smartconfig_get_rvd_data(rvd_data, sizeof(rvd_data)));
            ESP_LOGI(TAG, "RVD_DATA:");
            for (int i = 0; i < 33; i++)
            {
                printf("%02x ", rvd_data[i]);
            }
            printf("\n");
        }

        ESP_ERROR_CHECK(esp_wifi_disconnect());
        ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
        esp_wifi_connect();
    }
    else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE)
    {
        xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
    }
}

static void initialise_wifi(void)
{
    ESP_ERROR_CHECK(esp_netif_init());
    s_wifi_event_group = xEventGroupCreate();
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_start());
    esp_wifi_set_ps(DEFAULT_PS_MODE);
}

static void smartconfig_example_task(void *parm)
{
    EventBits_t uxBits;
    ESP_ERROR_CHECK(esp_smartconfig_set_type(SC_TYPE_ESPTOUCH_AIRKISS));
    smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_smartconfig_start(&cfg));
    while (1)
    {
        uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY);
        if (uxBits & CONNECTED_BIT)
        {
            ESP_LOGI(TAG, "WiFi Connected to ap");
        }
        if (uxBits & ESPTOUCH_DONE_BIT)
        {
            ESP_LOGI(TAG, "smartconfig over");
            esp_smartconfig_stop();
            vTaskDelete(ledc_task_1);
            vTaskDelete(NULL);
        }
    }
}

static void ledc_task(int LEDC_T_F_T , void * parm)
{
    ESP_LOGI("LEDC_START:","OK");

    if (LEDC_T_F_T == 0){LEDC_T_F_T = LEDC_TEST_FADE_TIME;}

    ledc_channel_config_t ledc_channel = {
    .channel = LEDC_HS_CH0_CHANNEL,
    .duty = 0,
    .gpio_num = LEDC_HS_CH0_GPIO,
    .speed_mode = LEDC_HS_MODE,
    .hpoint = 0,
    .timer_sel = LEDC_HS_TIMER};

    while (1)
    {


        ledc_set_fade_with_time(ledc_channel.speed_mode,
                                ledc_channel.channel, LEDC_TEST_DUTY, LEDC_T_F_T);
        ledc_fade_start(ledc_channel.speed_mode,
                        ledc_channel.channel, LEDC_FADE_NO_WAIT);

        vTaskDelay(LEDC_T_F_T / portTICK_PERIOD_MS);

        ledc_set_fade_with_time(ledc_channel.speed_mode,
                                ledc_channel.channel, 0, LEDC_T_F_T);
        ledc_fade_start(ledc_channel.speed_mode,
                        ledc_channel.channel, LEDC_FADE_NO_WAIT);

        vTaskDelay(LEDC_T_F_T / portTICK_PERIOD_MS);

        // ledc_set_duty(ledc_channel.speed_mode, ledc_channel.channel, LEDC_TEST_DUTY);
        // ledc_update_duty(ledc_channel.speed_mode, ledc_channel.channel);

        // vTaskDelay(LEDC_TEST_FADE_TIME / portTICK_PERIOD_MS);

        // ledc_set_duty(ledc_channel.speed_mode, ledc_channel.channel, 0);
        // ledc_update_duty(ledc_channel.speed_mode, ledc_channel.channel);

        // vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void ledc_test()
{
    // int ch;

    /*
     * Prepare and set configuration of timers
     * that will be used by LED Controller
     */
    ledc_timer_config_t ledc_timer = {
        .duty_resolution = LEDC_TIMER_13_BIT, // resolution of PWM duty
        .freq_hz = 5000,                      // frequency of PWM signal
        .speed_mode = LEDC_HS_MODE,           // timer mode
        .timer_num = LEDC_HS_TIMER,           // timer index
        .clk_cfg = LEDC_AUTO_CLK,             // Auto select the source clock
    };
    // Set configuration of timer0 for high speed channels
    ledc_timer_config(&ledc_timer);
    ledc_channel_config_t ledc_channel = {
        .channel = LEDC_HS_CH0_CHANNEL,
        .duty = 0,
        .gpio_num = LEDC_HS_CH0_GPIO,
        .speed_mode = LEDC_HS_MODE,
        .hpoint = 0,
        .timer_sel = LEDC_HS_TIMER};
    //设置
    ledc_channel_config(&ledc_channel);
    // Initialize fade service.
    ledc_fade_func_install(0);

    ledc_set_duty(ledc_channel.speed_mode, ledc_channel.channel, 0);
    ledc_update_duty(ledc_channel.speed_mode, ledc_channel.channel);

}

void app_main(void)
{

    // Initialize NVS
    nvs_err = nvs_flash_init();
    if (nvs_err == ESP_ERR_NVS_NO_FREE_PAGES || nvs_err == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        // NVS partition was truncated and needs to be erased
        // Retry nvs_flash_init
        ESP_ERROR_CHECK(nvs_flash_erase());
        nvs_err = nvs_flash_init();
    }
    ESP_ERROR_CHECK(nvs_err);

    esp_err_t err;
    nvs_handle_t my_handle;

    // uint8_t *out_value;
    // uint8_t out_value[33] = { 0 };

    // char *out_value;
    err = nvs_open("storage", NVS_READWRITE, &my_handle);
    if (err != ESP_OK)
    {
        printf("Error (%s) opening NVS handle!\n", esp_err_to_name(err));
    }
    else
    {
        nvs_stats_t nvs_stats;
        nvs_get_stats(NULL, &nvs_stats);
        ESP_LOGI(TAG, "Count: UsedEntries = (%d), FreeEntries = (%d), AllEntries = (%d)\n",
                 nvs_stats.used_entries, nvs_stats.free_entries, nvs_stats.total_entries);

        size_t required_size;
        nvs_get_str(my_handle, "SSID", NULL, &required_size);
        char *out_value = malloc(required_size);
        nvs_get_str(my_handle, "SSID", out_value, &required_size);
        // nvs_get_str(my_handle, "SSID", &out_value);
        if (out_value!= NULL){ESP_LOGI(TAG, " 读取到的数据 SSID：%s", out_value);}
        nvs_get_str(my_handle, "PASSWORD", NULL, &required_size);
        out_value = malloc(required_size);
        nvs_get_str(my_handle, "PASSWORD", out_value, &required_size);
        if (out_value!= NULL){ESP_LOGI(TAG, " 读取到的数据 PASSWORD：%s", out_value);}
        nvs_close(my_handle);
    }
    ledc_test();
    
    initialise_wifi();
}
